先来介绍下流程。
new/new[]
,他的流程是先开辟了一个空间,然后调用了构造函数。delete/delete[]
先调用析构函数,然后释放内存。
那么如果new/delete
和new[]/deletep[]
没有配对会出现什么问题。
对于这个问题我做了简单的实验。
非基础类型
也就是自己写的类。
先说下结论
delete
释放new[]
只会调用第一个的析构函数,导致内存泄漏。delete[]
释放new
会出bug
。
测试如下1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27class A{
int a;
public:
A():a(0xffff){
cout<<"A()\n";
}
~A(){
cout<<"~A()\n";
}
virtual void func(){
cout<<"func\n";
}
};
int main() {
A *b=new A[4];
delete [] b;
b =new A();
delete b;
b=new A[4];
delete b;
A *a=new A();
delete[] a;
return 0;
}
输出结果是1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18A()
A()
A()
A()
~A()
~A()
~A()
~A()//4个析构
A()
~A()//一个析构
A()
A()
A()
A()
~A()//一个析构
A()
Process finished with exit code -1073741819 (0xC0000005)//直接RE了
问题已经看出来,那么是为什呢。
我们去看看内存。
看到了吗,十分明显的一个数字,表示分配了多少个,后面很明显吧。一个虚函数指针,和一个值。
然后执行下一行释放内存。
看出内存的变化了吗,全部都释放掉了,前面那个4 也没了。
执行到这,用new
创建的明显没有一个表示多少个的。
执行下一行
然后没了被释放了。
执行后面错误的。
看看用delele
删除new[]
。
执行完后,他们全部都在,我都怀疑内存到底有没有释放。很显然全部都还没有释放掉。
然后后面那个为啥会RE不用我说了吧。
前面那个表述个数的数字这么大。。你想怎么搞。。。
小问题1不要慌
对于这个实验,如果把虚函数删掉,就不会是内存错误而是一个非常大的循环。至于为什么自己思考。
结论:
用new
参数的非基础类型,只会开辟一个对象内存空间,new[]
还会保存一个数组大小。delete[]
删除的时候会根据 删除地址前面的4个字节表示的数字大小来删除后面的对象。
为了保证正确性,我来做个有意思的事,我们来手写一个new[]
。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class A{
int a;
public:
A():a(0xffff){
cout<<"A()\n";
}
~A(){
cout<<"~A()\n";
}
virtual void func(){
cout<<"func\n";
}
};
int main() {
void *p=::operator new(sizeof(A)*4+sizeof(int));//这个可以用malloc来是一样的结果。
int *size=(int *)p;
*size=4;//这个设置一下大小
A *a=(A *)(size+1);//这个就是new[] 的实现
new(a)A();
new(a+1)A();
new(a+2)A();
new(a+3)A();
delete[] a;//显然这个是释放
return 0;
}
理论上delete []
也能手写实现,具体怎么实现读者自己动手操作,切忌拿来主义。
基本类型
对于基本类型和非基本类型有什么区别呢???,emmmm好像没啥区别。
但是他确实不一样。1
2
3
4
5
6
7
8
9
10
11
12
13
14int main() {
int *b=new int[4]{0xfff,0xffff,0xfff,0xfff};
delete [] b;
b =new int();
delete b;
b=new int[4]{0xfff,0xffff,0xfff,0xfff};
delete b;
int *a=new int();
delete[] a;
return 0;
}
运行这个你会发现,没有任何问题!!!!,确实就是,没有任何问题。
为什么会这样???。
我们照着前面那个的来。
看到了吗,没有那个表示数组长度的东西了。delete
之后内存变了。
后面的自己看,你会发现,用delet
删除new[]
完全没问题。。。delet[]
删除new
,好像也没啥问题。
那么问题来了,他们是怎么知道要释放多少内存的。
这个就比较复杂了。他和free
是一个性质,内存分配是交给操作系统完成的,具体怎么操作的还是和内核有关。
应该也能很清楚的看见,每次释放内存,改变的不仅仅是你需要的的内存大小,而是一整块内存。
所以用delete/delete[]
释放内存的时候,实际上是直接把那一块内存释放了。
结论:
对于基础类型,用delete/delete[]
删除实际上是一样的,主要原因应该还是因为,他没有调用构造和析构的必要。如果一个类里面封装了一个指针,然后new
了一个对象,如果不调用析构函数,就会出现内存泄漏。